package com.mune.gulimall.cart.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.mune.common.utils.R;
import com.mune.gulimall.cart.feign.ProductFeignService;
import com.mune.gulimall.cart.interceptor.LoginUserInterceptor;
import com.mune.gulimall.cart.service.CartService;
import com.mune.gulimall.cart.to.UserInfoTo;
import com.mune.gulimall.cart.vo.Cart;
import com.mune.gulimall.cart.vo.CartItem;
import com.mune.gulimall.cart.vo.SkuInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
  @Autowired
  RedisTemplate<String, Object> redisTemplate;

  private final String CART_PREFIX = "gulimall:cart:";

  @Autowired
  ProductFeignService productFeignService;

  @Autowired
  ThreadPoolExecutor executor;

  @Override
  public CartItem addCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
    BoundHashOperations<String, Object, Object> cartOps = getCartOps();
    CartItem cartItemRd = (CartItem) cartOps.get(skuId.toString());
    if (cartItemRd != null) {
      cartItemRd.setCount(cartItemRd.getCount() + 1);
      cartOps.put(skuId.toString(), cartItemRd);
      return cartItemRd;
    }
    CartItem cartItem = new CartItem();
    CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
      R skuInfo = productFeignService.getSkuInfo(skuId);
      SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
      });
      cartItem.setCount(num);
      cartItem.setImage(data.getSkuDefaultImg());
      cartItem.setTitle(data.getSkuTitle());
      cartItem.setSkuId(skuId);
      cartItem.setPrice(data.getPrice());
    }, executor);
    CompletableFuture<Void> getSkuSaleAttrValuesTask = CompletableFuture.runAsync(() -> {
      List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);
      cartItem.setSkuAttr(skuSaleAttrValues);
    }, executor);
    CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttrValuesTask).get();
    cartOps.put(skuId.toString(), cartItem);
    return cartItem;
  }

  @Override
  public CartItem getCartItemBySkuId(Long skuId) {

    BoundHashOperations<String, Object, Object> cartOps = getCartOps();
    CartItem cartItem = (CartItem) cartOps.get(skuId.toString());
    return cartItem;

  }

  @Override
  public Cart getCart() throws ExecutionException, InterruptedException {

    UserInfoTo userInfoTo = LoginUserInterceptor.toThreadLocal.get();
    Cart cart = new Cart();
    if (userInfoTo.getUserId() != null) {
      // 登录了
      String cartOnlineKey = CART_PREFIX + userInfoTo.getUserId();
      // 如果Redis中有当前用户的临时购物车，则进行合并操作
      List<CartItem> cartItems = getCartItems(CART_PREFIX + userInfoTo.getUserKey());
      if (cartItems != null) {
        for (CartItem cartItem : cartItems) {
          addCart(cartItem.getSkuId(), cartItem.getCount());
        }
        clearCart(CART_PREFIX + userInfoTo.getUserKey());
      }
      List<CartItem> cartOnlineItems = getCartItems(cartOnlineKey);
      cart.setItems(cartOnlineItems);
    } else {
      String cartKey = CART_PREFIX + userInfoTo.getUserKey();
      List<CartItem> cartItems = getCartItems(cartKey);
      // 没登录
      cart.setItems(cartItems);
    }
    return cart;
  }

  @Override
  public void clearCart(String cartKey) {
    redisTemplate.delete(cartKey);
  }

  @Override
  public void checkItem(Long skuId, Integer check) {
    //查询购物项
    CartItem cartItem = getCartItemBySkuId(skuId);
    //修改商品状态
    cartItem.setCheck(check == 1);

    BoundHashOperations<String, Object, Object> redisBindOps = getCartOps();
    redisBindOps.put(skuId.toString(), cartItem);
  }

  /**
   * 修改购物项数量
   *
   * @param skuId
   * @param num
   */
  @Override
  public void updateItemCount(Long skuId, Integer num) {

    //查询购物车里面的商品
    CartItem cartItem = getCartItemBySkuId(skuId);
    cartItem.setCount(num);
    cartItem.setTotalPrice(cartItem.getPrice().multiply(new BigDecimal(num)));

    BoundHashOperations<String, Object, Object> redisBindOps = getCartOps();
    redisBindOps.put(skuId.toString(), cartItem);
  }


  /**
   * 删除购物项
   *
   * @param skuId
   */
  @Override
  public void deleteItem(Integer skuId) {
    BoundHashOperations<String, Object, Object> redisBindOps = getCartOps();
    redisBindOps.delete(skuId.toString());
  }

  @Override
  public List<CartItem> getUserCartItems() {
    UserInfoTo userInfoTo = LoginUserInterceptor.toThreadLocal.get();
    if (userInfoTo.getUserId() == null) return null;
    List<CartItem> cartItems = getCartItems(CART_PREFIX + userInfoTo.getUserId());
    return cartItems.stream().filter(CartItem::getCheck).map(item -> {
      R price = productFeignService.getPrice(item.getSkuId());
      if (price.getCode() == 0) {
        item.setPrice(new BigDecimal((String) price.get("data")));
      }
      return item;
    }).collect(Collectors.toList());

  }

  private BoundHashOperations<String, Object, Object> getCartOps() {
    UserInfoTo userInfoTo = LoginUserInterceptor.toThreadLocal.get();
    String cartKey = "";
    if (userInfoTo.getUserId() != null) {
      cartKey = CART_PREFIX + userInfoTo.getUserId();
    } else {
      cartKey = CART_PREFIX + userInfoTo.getUserKey();
    }
    BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(cartKey);
    return ops;
  }

  private List<CartItem> getCartItems(String cartKey) {
    BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(cartKey);
    List<Object> values = ops.values();
    if (values != null && values.size() > 0) {
      List<CartItem> collect = values.stream().map((item) -> (CartItem) item).collect(Collectors.toList());
      return collect;
    }
    return null;
  }
}
